﻿#include "framework.h"
#include "Const.h"
#include "Snake.h"
#include "SnakeBody.h"

// 函数声明
static void Snake_Delete(Snake*);
static void Snake_Eat(Snake*, SnakeBody*);
static BOOL Snake_Move(Snake*);
static BOOL Snake_PointIsOnMe(Snake*, POINT);
static void Snake_Turn(Snake*, UINT);

/**
 * 创建蛇。
 * @param drawer    绘图者
 * @return 蛇
 */
Snake* New_Snake(HDC drawer) {
    Snake* snake = (Snake*)calloc(1, sizeof(Snake));
    POINT caudaPos = {0, 0};

    // 初始化函数
    snake->deleteSelf = Snake_Delete;
    snake->eat = Snake_Eat;
    snake->move = Snake_Move;
    snake->pointIsOnMe = Snake_PointIsOnMe;
    snake->turn = Snake_Turn;

    // 初始化数据
    snake->mHead = snake->mCauda = New_SnakeBody(drawer, caudaPos, SNAKE);
    snake->mDirection = VK_DOWN;

    return snake;
}

/**
 * 删除蛇。
 * @param snake 蛇
 */
static void Snake_Delete(Snake* snake) {
    // 从蛇尾开始，删除所有蛇身
    SnakeBody* snakeBody = snake->mCauda->mNext;
    while (snakeBody != NULL) {
        snake->mCauda->deleteSelf(&snake->mCauda);
        snake->mCauda = snakeBody;
        snakeBody = snakeBody->mNext;
    }
    snake->mHead->deleteSelf(&snake->mHead);
}

/**
 * 吃果子，即将果子放到蛇尾。
 * @param snake 蛇
 * @param apple 果子
 */
static void Snake_Eat(Snake* snake, SnakeBody* apple) {
    // 设置吃到的果子的位置
    apple->mPosition.x = snake->mHead->mPosition.x;
    apple->mPosition.y = snake->mHead->mPosition.y;
    switch (snake->mDirection) {
        case VK_DOWN:
            apple->mPosition.y++;
            break;
        case VK_LEFT:
            apple->mPosition.x--;
            break;
        case VK_RIGHT:
            apple->mPosition.x++;
            break;
        case VK_UP:
            apple->mPosition.y--;
            break;
    }

    // 改变果子的类型
    apple->setType(apple, SNAKE);

    // 重绘蛇头和果子
    snake->mHead->draw(snake->mHead);
    apple->draw(apple);

    // 将吃到的果子变成蛇头
    snake->mHead->mNext = apple;
    snake->mHead = apple;
}

/**
 * 移动蛇。
 * @param snake 蛇
 * @return 蛇头碰到自身或游戏区域边界则返回 TRUE，否则返回 FALSE
 */
static BOOL Snake_Move(Snake* snake) {
    SnakeBody* snakeBody = NULL;
    POINT point;
    point.x = snake->mHead->mPosition.x;
    point.y = snake->mHead->mPosition.y;

    // 计算蛇尾的新位置并抹除蛇尾
    switch (snake->mDirection) {
        case VK_DOWN:
            if (point.y + 1 >= GAME_BOARD_GRID_COUNT_V) {
                // 如果蛇头超出游戏区域下边界，则返回
                return TRUE;
            }
            snake->mCauda->erase(snake->mCauda);
            point.y++;
            break;
        case VK_LEFT:
            if (point.x - 1 < 0) {
                // 如果蛇头超出游戏区域左边界，则返回
                return TRUE;
            }
            snake->mCauda->erase(snake->mCauda);
            point.x--;
            break;
        case VK_RIGHT:
            if (point.x + 1 >= GAME_BOARD_GRID_COUNT_H) {
                // 如果蛇头超出游戏区域右边界，则返回
                return TRUE;
            }
            snake->mCauda->erase(snake->mCauda);
            point.x++;
            break;
        case VK_UP:
            if (point.y - 1 < 0) {
                // 如果蛇头超出游戏区域上边界，则返回
                return TRUE;
            }
            snake->mCauda->erase(snake->mCauda);
            point.y--;
            break;
    }
    snake->mCauda->mPosition = point;

    // 将蛇尾的下一个结点设置为蛇尾、原蛇尾设置成蛇头
    snake->mHead->mNext = snake->mCauda;
    snake->mHead = snake->mCauda;
    snake->mCauda = snake->mCauda->mNext;
    snake->mHead->mNext = NULL;
    snake->mHead->draw(snake->mHead);

    // 检查蛇头是否碰到蛇身
    for (snakeBody = snake->mCauda; snakeBody != snake->mHead; snakeBody = snakeBody->mNext) {
        if (snakeBody->mPosition.x == snake->mHead->mPosition.x &&
            snakeBody->mPosition.y == snake->mHead->mPosition.y) {
            return TRUE;
        }
    }

    return FALSE;
}

/**
 * 检查某点是否与蛇身位置重叠。
 * @param snake 蛇
 * @param point 被检查的点
 * @return 点与蛇身位置重叠则返回 TRUE ，否则返回 FALSE
 */
static BOOL Snake_PointIsOnMe(Snake* snake, POINT point) {
    SnakeBody* snakeBody = NULL;
    for (snakeBody = snake->mCauda; snakeBody != NULL; snakeBody = snakeBody->mNext) {
        if (snakeBody->mPosition.x == point.x && snakeBody->mPosition.y == point.y) {
            return TRUE;
        }
    }
    return FALSE;
}

/**
 * 蛇转弯。
 * @param snake     蛇
 * @param direction 方向
 */
static void Snake_Turn(Snake* snake, UINT direction) {
    // 如果方向与当前方向相反（想要直接调头），则直接返回
    if (abs((int)(direction - snake->mDirection)) == 2) {
        return;
    }
    snake->mDirection = direction;
}
